// Burner Input Dialog module
// added default mapping and autofire settings, by regret
#include "burner.h"

HWND hInpdDlg = NULL;						// Handle to the Input Dialog
static HWND hInpdList = NULL;
static unsigned char* lastVal = NULL;		// Last input values/defined
static int lastValDefined = 0;				//

static HWND hInpdGi = NULL, hInpdPci = NULL, hInpdAnalog = NULL;	// Combo boxes

#ifndef NO_AUTOFIRE
// autofire map
struct AutoFireInfo {
	int player;
	int button;
};
map<int, AutoFireInfo> autofireMap;

// ==> autofire setting, added by regret
static int inpdAutofireInit()
{
	if (!hInpdList || !hInpdDlg) {
		return 1;
	}

	// init autofire checkboxes
	AutoFireInfo* autofireInfo = NULL;
	map<int, AutoFireInfo>::iterator iter = autofireMap.begin();
	for (; iter != autofireMap.end(); iter++) {
		autofireInfo = &iter->second;
		if (autofire.get(autofireInfo->player, autofireInfo->button)) {
			ListView_SetCheckState(hInpdList, iter->first, TRUE);
		}
	}

	// autofire delay control
	wchar_t delayStr[32] = L"";
	for (unsigned int i = 1; i < 100; i++) {
		swprintf(delayStr, L"%d", i);
		SendDlgItemMessage(hInpdDlg, IDC_INPD_AUTOFIRE_DELAY, CB_ADDSTRING, 0, (LPARAM)delayStr);
	}
	SendDlgItemMessage(hInpdDlg, IDC_INPD_AUTOFIRE_DELAY, CB_SETCURSEL, (WPARAM)(autofire.delay - 1), 0);

	return 0;
}

static void inpdAutofireExit()
{
	if (!hInpdDlg) {
		return;
	}

	// autofire delay
	autofire.delay = SendDlgItemMessage(hInpdDlg, IDC_INPD_AUTOFIRE_DELAY, CB_GETCURSEL, 0, 0) + 1;

	// autofire state
	AutoFireInfo* autofireInfo = NULL;
	map<int, AutoFireInfo>::iterator iter = autofireMap.begin();
	for (; iter != autofireMap.end(); iter++) {
		autofireInfo = &iter->second;
		BOOL state = ListView_GetCheckState(hInpdList, iter->first);
		autofire.set(autofireInfo->player, autofireInfo->button, state);
	}
	autofireMap.clear();
}
// <== autofire setting
#endif

// Update which input is using which PC input
static int inpdUseUpdate()
{
	if (hInpdList == NULL) {
		return 1;
	}

	unsigned int i, j = 0;
	GameInp* pgi = NULL;

	// Update the values of all the inputs
	for (i = 0, pgi = gameInput; i < gameInpCount; i++, pgi++) {
		if (pgi->Input.pVal == NULL) {
			continue;
		}

		wchar_t* pszVal = inpToDesc(pgi);

		LVITEM LvItem;
		memset(&LvItem, 0, sizeof(LvItem));
		LvItem.mask = LVIF_TEXT;
		LvItem.iItem = j;
		LvItem.iSubItem = 1;
		LvItem.pszText = pszVal;
		ListView_SetItem(hInpdList, &LvItem);

		j++;
	}

	for (i = 0, pgi = gameInput + gameInpCount; i < macroCount; i++, pgi++) {
		if (pgi->nInput & GIT_GROUP_MACRO) {
			wchar_t* pszVal = inpMacroToDesc(pgi);

			LVITEM LvItem;
			memset(&LvItem, 0, sizeof(LvItem));
			LvItem.mask = LVIF_TEXT;
			LvItem.iItem = j;
			LvItem.iSubItem = 1;
			LvItem.pszText = pszVal;
			ListView_SetItem(hInpdList, &LvItem);
		}

		j++;
	}

	return 0;
}

int inpdUpdate()
{
	if (hInpdList == NULL || lastVal == NULL) {
		return 1;
	}

	unsigned int i, j = 0;
	GameInp* pgi = NULL;
	unsigned char* plv = NULL;
	unsigned short nThisVal;

	// Update the values of all the inputs
	for (i = 0, pgi = gameInput, plv = lastVal; i < gameInpCount; i++, pgi++, plv++) {
		wchar_t szVal[16];

		if (pgi->nType == 0) {
			continue;
		}

		if (pgi->nType & BIT_GROUP_ANALOG) {
			if (runPause) {									// Update lastVal
				nThisVal = pgi->Input.nVal;
			} else {
				nThisVal = *pgi->Input.pShortVal;
			}

			if (lastValDefined && (pgi->nType != BIT_ANALOG_REL || nThisVal) && pgi->Input.nVal == *((unsigned short*)plv)) {
				j++;
				continue;
			}

			*((unsigned short*)plv) = nThisVal;
		} else {
			if (runPause) {									// Update lastVal
				nThisVal = pgi->Input.nVal;
			} else {
				nThisVal = *pgi->Input.pVal;
			}

			if (lastValDefined && pgi->Input.nVal == *plv) {	// hasn't changed
				j++;
				continue;
			}

			*plv = nThisVal;
		}

		switch (pgi->nType) {
			case BIT_DIGITAL: {
				if (nThisVal == 0) {
					szVal[0] = 0;
				} else {
					if (nThisVal == 1) {
						wcscpy(szVal, L"ON");
					} else {
						swprintf(szVal, L"0x%02X", nThisVal);
					}
				}
				break;
			}
			case BIT_ANALOG_ABS: {
				swprintf(szVal, L"0x%02X", nThisVal >> 8);
				break;
			}
			case BIT_ANALOG_REL: {
				if (nThisVal == 0) {
					szVal[0] = 0;
				}
				if ((short)nThisVal < 0) {
					swprintf(szVal, L"%d", ((short)nThisVal) >> 8);
				}
				if ((short)nThisVal > 0) {
					swprintf(szVal, L"+%d", ((short)nThisVal) >> 8);
				}
				break;
			}
			default: {
				swprintf(szVal, L"0x%02X", nThisVal);
			}
		}

		LVITEM LvItem;
		memset(&LvItem, 0, sizeof(LvItem));
		LvItem.mask = LVIF_TEXT;
		LvItem.iItem = j;
		LvItem.iSubItem = 2;
		LvItem.pszText = szVal;
		ListView_SetItem(hInpdList, &LvItem);

		j++;
	}

	lastValDefined = 1; // lastVal is now defined

	return 0;
}

static int InpdListBegin()
{
	if (hInpdList == NULL) {
		return 1;
	}

	// Full row select style:
#ifndef NO_AUTOFIRE
	ListView_SetExtendedListViewStyle(hInpdList, LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
#else
	ListView_SetExtendedListViewStyle(hInpdList, LVS_EX_FULLROWSELECT);
#endif

	// Make column headers
	LVCOLUMN LvCol;
	memset(&LvCol, 0, sizeof(LvCol));
	LvCol.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;

	LvCol.cx = 144;
	LvCol.pszText = localiseStringEx(IDS_INPUT_INPUT);
	ListView_InsertColumn(hInpdList, 0, &LvCol);

	LvCol.cx = 144;
	LvCol.pszText = localiseStringEx(IDS_INPUT_MAPPING);
	ListView_InsertColumn(hInpdList, 1, &LvCol);

	LvCol.cx = 40;
	LvCol.pszText = localiseStringEx(IDS_INPUT_STATE);
	ListView_InsertColumn(hInpdList, 2, &LvCol);

	return 0;
}

// Make a list view of the game inputs
int inpdListMake(int build)
{
	if (hInpdList == NULL) {
		return 1;
	}

	unsigned int j = 0;
	lastValDefined = 0;

	if (build)	{
		ListView_DeleteAllItems(hInpdList);
	}

	// get button info
	int nButtons = 0; // buttons per player
	int player = 0;
	int nPlayerTemp = 0;

	// Add all the input names to the list
	for (unsigned int i = 0; i < gameInpCount; i++) {
		// Get the name of the input
		struct BurnInputInfo bii;
		bii.szName = NULL;
		BurnDrvGetInputInfo(&bii, i);

		// skip unused inputs
		if (bii.pVal == NULL) {
			continue;
		}
		if (bii.szName == NULL)	{
			bii.szName = "";
		}

#ifndef NO_AUTOFIRE
		// init autofire info
		if ((bii.szName[0] == 'P' || bii.szName[0] == 'p')
			&& bii.szName[1] >= '1' && bii.szName[1] <= '4') {
			player = bii.szName[1] - '1';

			if (player != nPlayerTemp) {
				// if it is next player
				nButtons = 0;
			}
			if (!strncmp(" fire", bii.szInfo + 2, 5) && nButtons < MAX_AUTOFIRE_BUTTONS) {
				AutoFireInfo info;
				info.button = nButtons;
				info.player = player;
				autofireMap[j] = info;
				nButtons++;
			}

			nPlayerTemp = player;
		}
#endif

		// add to list
		LVITEM LvItem;
		memset(&LvItem, 0, sizeof(LvItem));
		LvItem.mask = LVIF_TEXT | LVIF_PARAM;
		LvItem.iItem = j;
		LvItem.iSubItem = 0;
		LvItem.pszText = AtoW(bii.szName);
		LvItem.lParam = (LPARAM)i;

		if (build) {
			ListView_InsertItem(hInpdList, &LvItem);
		} else {
			ListView_SetItem(hInpdList, &LvItem);
		}

		j++;
	}

	GameInp* pgi = gameInput + gameInpCount;
	for (unsigned int i = 0; i < macroCount; i++, pgi++) {
		if (pgi->nInput & GIT_GROUP_MACRO) {
			LVITEM LvItem;
			memset(&LvItem, 0, sizeof(LvItem));
			LvItem.mask = LVIF_TEXT | LVIF_PARAM;
			LvItem.iItem = j;
			LvItem.iSubItem = 0;
			LvItem.pszText = AtoW(pgi->Macro.szName);
			LvItem.lParam = (LPARAM)j;

			if (build) {
				ListView_InsertItem(hInpdList, &LvItem);
			} else {
				ListView_SetItem(hInpdList, &LvItem);
			}
		}

		j++;
	}

	inpdUseUpdate();

	return 0;
}

static void disablePresets()
{
	EnableWindow(hInpdPci, FALSE);
	EnableWindow(hInpdAnalog, FALSE);
	EnableWindow(GetDlgItem(hInpdDlg, IDC_INPD_DEFAULT), FALSE);
	EnableWindow(GetDlgItem(hInpdDlg, IDC_INPD_USE), FALSE);
}

static void initComboboxes()
{
	wchar_t szLabel[1024];

	for (int i = 0; i < 4; i++) {
		swprintf(szLabel, localiseStringEx(IDS_INPUT_INP_PLAYER), i + 1);
		SendMessage(hInpdGi, CB_ADDSTRING, 0, (LPARAM)szLabel);
	}

	SendMessage(hInpdPci, CB_ADDSTRING, 0, (LPARAM)localiseStringEx(IDS_INPUT_INP_KEYBOARD));
	for (int i = 0; i < 3; i++) {
		swprintf(szLabel, localiseStringEx(IDS_INPUT_INP_JOYSTICK), i);
		SendMessage(hInpdPci, CB_ADDSTRING, 0, (LPARAM)szLabel);
	}
	SendMessage(hInpdPci, CB_ADDSTRING, 0, (LPARAM)localiseStringEx(IDS_INPUT_INP_XARCADEL));
	SendMessage(hInpdPci, CB_ADDSTRING, 0, (LPARAM)localiseStringEx(IDS_INPUT_INP_XARCADER));
	SendMessage(hInpdPci, CB_ADDSTRING, 0, (LPARAM)localiseStringEx(IDS_INPUT_INP_HOTRODL));
	SendMessage(hInpdPci, CB_ADDSTRING, 0, (LPARAM)localiseStringEx(IDS_INPUT_INP_HOTRODR));

	// Scan presets directory for .ini files and add them to the list
	WIN32_FIND_DATA findData;
	HANDLE search = FindFirstFile(L"config\\presets\\*.ini", &findData);
	if (search != INVALID_HANDLE_VALUE) {
		do {
			findData.cFileName[wcslen(findData.cFileName) - 4] = 0;
			SendMessage(hInpdPci, CB_ADDSTRING, 0, (LPARAM)findData.cFileName);
		} while (FindNextFile(search, &findData) != 0);

		FindClose(search);
	}
}

static int inpdInit()
{
	hInpdList = GetDlgItem(hInpdDlg, IDC_INPD_LIST);
	if (hInpdList == NULL) {
		return 1;
	}

	// Allocate a last val array for the last input values
	int nMemLen = gameInpCount * sizeof(char);
	lastVal = new unsigned char[nMemLen];
	if (lastVal == NULL) {
		return 1;
	}
	memset(lastVal, 0, nMemLen);

	InpdListBegin();
	inpdListMake(1);
#ifndef NO_AUTOFIRE
	inpdAutofireInit();
#endif

	// Init the Combo boxes
	hInpdGi = GetDlgItem(hInpdDlg, IDC_INPD_GI);
	hInpdPci = GetDlgItem(hInpdDlg, IDC_INPD_PCI);
	hInpdAnalog = GetDlgItem(hInpdDlg, IDC_INPD_ANALOG);
	initComboboxes();

	disablePresets();

	return 0;
}

static int inpdExit()
{
#ifndef NO_AUTOFIRE
	inpdAutofireExit();
#endif
	// save what we want
	configGameSave(true); // save game config

	// Exit the Combo boxes
	hInpdPci = NULL;
	hInpdGi = NULL;
	hInpdAnalog = NULL;

	if (lastVal != NULL) {
		delete [] lastVal;
		lastVal = NULL;
	}
	hInpdList = NULL;
	hInpdDlg = NULL;

	gameInpCheckMouse();
	return 0;
}

static void gameInpConfigOne(int player, int pcDev, int analog, GameInp* pgi, const char* szi)
{
	switch (pcDev) {
		case  0:
			gamcPlayer(pgi, szi, player, -1);						// Keyboard
			gamcAnalogKey(pgi, szi, player, analog);
			gamcMisc(pgi, szi, player);
			break;
		case  1:
			gamcPlayer(pgi, szi, player, 0);						// Joystick 1
			gamcAnalogJoy(pgi, szi, player, 0, analog);
			gamcMisc(pgi, szi, player);
			break;
		case  2:
			gamcPlayer(pgi, szi, player, 1);						// Joystick 2
			gamcAnalogJoy(pgi, szi, player, 1, analog);
			gamcMisc(pgi, szi, player);
			break;
		case  3:
			gamcPlayer(pgi, szi, player, 2);						// Joystick 3
			gamcAnalogJoy(pgi, szi, player, 2, analog);
			gamcMisc(pgi, szi, player);
			break;
		case  4:
			gamcPlayerHotRod(pgi, szi, player, 0x10, analog);		// X-Arcade left side
			gamcMisc(pgi, szi, -1);
			break;
		case  5:
			gamcPlayerHotRod(pgi, szi, player, 0x11, analog);		// X-Arcade right side
			gamcMisc(pgi, szi, -1);
			break;
		case  6:
			gamcPlayerHotRod(pgi, szi, player, 0x00, analog);		// HotRod left side
			gamcMisc(pgi, szi, -1);
			break;
		case  7:
			gamcPlayerHotRod(pgi, szi, player, 0x01, analog);		// HotRod right size
			gamcMisc(pgi, szi, -1);
			break;
	}
}

// Configure some of the game input
static int gameInpConfig(int player, int pcDev, int analog)
{
	GameInp* pgi = NULL;
	unsigned int i;

	for (i = 0, pgi = gameInput; i < gameInpCount; i++, pgi++) {
		struct BurnInputInfo bii;

		// Get the extra info about the input
		bii.szInfo = NULL;
		BurnDrvGetInputInfo(&bii, i);
		if (bii.pVal == NULL) {
			continue;
		}
		if (bii.szInfo == NULL) {
			bii.szInfo = "";
		}
		gameInpConfigOne(player, pcDev, analog, pgi, bii.szInfo);
	}

	for (i = 0; i < macroCount; i++, pgi++) {
		gameInpConfigOne(player, pcDev, analog, pgi, pgi->Macro.szName);
	}

	gameInpCheckLeftAlt();

	return 0;
}

// List item activated; find out which one
static int listItemActivate()
{
	int nSel = ListView_GetNextItem(hInpdList, -1, LVNI_SELECTED);
	if (nSel < 0) {
		return 1;
	}

	// Get the corresponding input
	LVITEM LvItem;
	LvItem.mask = LVIF_PARAM;
	LvItem.iItem = nSel;
	LvItem.iSubItem = 0;
	ListView_GetItem(hInpdList, &LvItem);

	nSel = LvItem.lParam;
	if (nSel >= (int)(gameInpCount + macroCount)) {	// out of range
		return 1;
	}

	struct BurnInputInfo bii;
	bii.nType = 0;
	BurnDrvGetInputInfo(&bii, nSel);
	if (bii.pVal == NULL) {
		return 1;
	}

	dialogManager.remove(IDD_INPS);
	dialogManager.remove(IDD_INPC);

	if (bii.nType & BIT_GROUP_CONSTANT) {
		// Dip switch is a constant - change it
		inpcInput = nSel;
		inpcCreate();
	} else {
		if (gameInput[nSel].nInput == GIT_MACRO_CUSTOM) {
#if 0
			InpMacroCreate(nSel);
#endif
		} else {
			// Assign to a key
			inpsInput = nSel;
			inpsCreate();
		}
	}

	gameInpCheckLeftAlt();

	return 0;
}

static int newMacroButton()
{
#if 0
	LVITEM LvItem;
	int nSel;

	dialogDelete(IDD_INPS);
	dialogDelete(IDD_INPC);

	nSel = ListView_GetNextItem(hInpdList, -1, LVNI_SELECTED);
	if (nSel < 0) {
		nSel = -1;
	}

	// Get the corresponding input
	LvItem.mask = LVIF_PARAM;
	LvItem.iItem = nSel;
	LvItem.iSubItem = 0;
	ListView_GetItem(hInpdList, &LvItem);
	nSel = LvItem.lParam;

	if (nSel >= (int)gameInpCount && nSel < (int)(gameInpCount + macroCount)) {
		if (gameInput[nSel].nInput != GIT_MACRO_CUSTOM) {
			nSel = -1;
		}
	} else {
		nSel = -1;
	}

	InpMacroCreate(nSel);
#endif

	return 0;
}

static int deleteInput(unsigned int i)
{
	if (i >= gameInpCount) {
		if (i < gameInpCount + macroCount) {	// Macro
			gameInput[i].Macro.nMode = 0;
		} else { 								// out of range
			return 1;
		}
	} else {									// "True" input
		struct BurnInputInfo bii;
		bii.nType = BIT_DIGITAL;
		BurnDrvGetInputInfo(&bii, i);
		if (bii.pVal == NULL) {
			return 1;
		}
		if (bii.nType & BIT_GROUP_CONSTANT) {	// Don't delete dip switches
			return 1;
		}

		gameInput[i].nInput = 0;
	}

	gameInpCheckLeftAlt();

	return 0;
}

// List item(s) deleted; find out which one(s)
static int listItemDelete()
{
	int nStart = -1;
	LVITEM LvItem;
	int ret;

	while ((ret = ListView_GetNextItem(hInpdList, nStart, LVNI_SELECTED)) != -1) {
		nStart = ret;

		// Get the corresponding input
		LvItem.mask = LVIF_PARAM;
		LvItem.iItem = ret;
		LvItem.iSubItem = 0;
		ListView_GetItem(hInpdList, &LvItem);
		ret = LvItem.lParam;

		deleteInput(ret);
	}

	inpdListMake(0); // refresh view
	return 0;
}

static int initAnalogOptions(int nGi, int nPci)
{
	// init analog options dialog
	int analog = -1;
	if (nPci == (playerDefaultControls[nGi] & 0x0F)) {
		analog = playerDefaultControls[nGi] >> 4;
	}

	SendMessage(hInpdAnalog, CB_RESETCONTENT, 0, 0);
	if (nPci >= 1 && nPci <= 3) {
		// Absolute mode only for joysticks
		SendMessage(hInpdAnalog, CB_ADDSTRING, 0, (LPARAM)(LPARAM)localiseStringEx(IDS_INPUT_ANALOG_ABS));
	} else {
		if (analog > 0) {
			analog--;
		}
	}
	SendMessage(hInpdAnalog, CB_ADDSTRING, 0, (LPARAM)(LPARAM)localiseStringEx(IDS_INPUT_ANALOG_AUTO));
	SendMessage(hInpdAnalog, CB_ADDSTRING, 0, (LPARAM)(LPARAM)localiseStringEx(IDS_INPUT_ANALOG_NORMAL));

	SendMessage(hInpdAnalog, CB_SETCURSEL, (WPARAM)analog, 0);

	return 0;
}

int usePreset(bool makeDefault)
{
	int nGi, nPci, analog = 0;
	wchar_t filename[MAX_PATH] = L"config\\presets\\";

	nGi = SendMessage(hInpdGi, CB_GETCURSEL, 0, 0);
	if (nGi == CB_ERR) {
		return 1;
	}
	nPci = SendMessage(hInpdPci, CB_GETCURSEL, 0, 0);
	if (nPci == CB_ERR) {
		return 1;
	}

	if (nPci <= 7) {
		// Determine analog option
		analog = SendMessage(hInpdAnalog, CB_GETCURSEL, 0, 0);
		if (analog == CB_ERR) {
			return 1;
		}

		if (nPci == 0 || nPci > 3) {				// No "Absolute" option for keyboard or X-Arcade/HotRod controls
			analog++;
		}

		gameInpConfig(nGi, nPci, analog);			// Re-configure inputs
	} else {
		// Find out the filename of the preset ini
		SendMessage(hInpdPci, CB_GETLBTEXT, nPci, (LPARAM)(filename + wcslen(filename)));
		wcscat(filename, L".ini");

		gameInputAutoIni(nGi, filename, true);	// Read inputs from file

		// Make sure all inputs are defined
		for (unsigned int i = 0, j = 0; i < gameInpCount; i++) {
			if (gameInput[i].Input.pVal == NULL) {
				continue;
			}

			if (gameInput[i].nInput == 0) {
				deleteInput(j);
			}

			j++;
		}

		nPci = 0x0F;
	}

	SendMessage(hInpdAnalog, CB_SETCURSEL, (WPARAM)-1, 0);
	SendMessage(hInpdPci, CB_SETCURSEL, (WPARAM)-1, 0);
	SendMessage(hInpdGi, CB_SETCURSEL, (WPARAM)-1, 0);

	disablePresets();

	if (makeDefault) {
		playerDefaultControls[nGi] = nPci | (analog << 4);
		wcscpy(playerDefaultIni[nGi], filename);
	}

	gameInpCheckLeftAlt();

	return 0;
}

// ==> default input mapping, added by regret
wchar_t* defaultInpFilename = L"config\\presets\\default.ini";
wchar_t* defaultNeoInpFilename = L"config\\presets\\default_neo.ini";
wchar_t* defaultCpsInpFilename = L"config\\presets\\default_cps.ini";

static wchar_t* getDefaultInputFilename()
{
	wchar_t* fileName = defaultInpFilename;

#if 1
	int flag = (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK);

	if (flag == HARDWARE_SNK_NEOGEO) {
		fileName = defaultNeoInpFilename;
	}
	else if (flag == HARDWARE_CAPCOM_CPS1
		|| flag == HARDWARE_CAPCOM_CPS1_QSOUND
		|| flag == HARDWARE_CAPCOM_CPS1_GENERIC
		|| flag == HARDWARE_CAPCOM_CPSCHANGER
		|| flag == HARDWARE_CAPCOM_CPS2
		|| flag == HARDWARE_CAPCOM_CPS3) {
		fileName = defaultCpsInpFilename;
	}
#endif

	return fileName;
}

static int saveDefaultInput()
{
	wchar_t* fileName = getDefaultInputFilename();
	FILE* h = _wfopen(fileName, L"wt");
	if (h == NULL) {
		return 1;
	}

	// Write version number
	fwprintf(h, L"version 0x%06X\n\n", burnVersion);
	gameInpWrite(h, false);
	if (h) {
		fclose(h);
	}

	return 0;
}

int loadDefaultInput()
{
	wchar_t* fileName = getDefaultInputFilename();

	// Read default inputs from file
	for (int player = 0; player < maxPlayers; player++) {
		gameInputAutoIni(player, fileName, true);
	}
	return 0;
}
// <== default input mapping

// ==> save preset, added by regret
static int savePreset(HWND hDlg)
{
	// save file with your input name
	swprintf(commonChoice, L"preset");
	if (commonFileDialog(hDlg, true, L"Save preset file", L"Preset files(*.ini)\0*.ini\0\0", 0, L"ini")) {
		return 1;
	}

	FILE* h = _wfopen(commonChoice, L"wt");
	if (h == NULL) {
		return 1;
	}

	// Write version number
	fwprintf(h, L"version 0x%06X\n\n", burnVersion);
	gameInpWrite(h, false);
	if (h) {
		fclose(h);
	}

	return 0;
}

static int onRButtonUp(HWND hwnd, int x, int y)
{
	if (hwnd == hScrnWnd || vidFullscreen) {
		return 1;
	}

	RECT rc;
	GetWindowRect(GetDlgItem(hwnd, IDC_INPD_USE), &rc);

	POINT pt;
	pt.x = x; pt.y = y;
	ClientToScreen(hwnd, &pt);

	if (pt.x > rc.left && pt.x < rc.right && pt.y > rc.top && pt.y < rc.bottom) {
		savePreset(hwnd);
		return 0;
	}

	return 1;
}
// <== save preset

static INT_PTR CALLBACK inpDialogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	if (msg == WM_INITDIALOG) {
		hInpdDlg = hDlg;
		inpdInit();
		return TRUE;
	}

	if (msg == WM_CLOSE) {
		EnableWindow(hScrnWnd, TRUE);
		DestroyWindow(hInpdDlg);
		dialogManager.remove(IDD_INPD);
		return FALSE;
	}

	if (msg == WM_DESTROY) {
		inpdExit();
		return FALSE;
	}

	if (msg == WM_COMMAND) {
		int Id = LOWORD(wParam);
		int Notify = HIWORD(wParam);

		if (Id == IDOK && Notify == BN_CLICKED) {
			listItemActivate();
			return FALSE;
		}
		if (Id == IDCANCEL && Notify == BN_CLICKED) {
			SendMessage(hDlg, WM_CLOSE, 0, 0);
			return FALSE;
		}

		if (Id == IDC_INPD_NEWMACRO && Notify == BN_CLICKED) {
			newMacroButton();
			return FALSE;
		}

		if (Id == IDC_INPD_USE && Notify == BN_CLICKED) {
			usePreset(false);
			inpdListMake(0);	// refresh view
			return FALSE;
		}

		if (Id == IDC_INPD_DEFAULT && Notify == BN_CLICKED) {
			usePreset(true);
			inpdListMake(0);	// refresh view
			return FALSE;
		}

		// ==> default input mapping, added by regret
		if (Id == IDC_INPD_SAVEDEF && Notify == BN_CLICKED) {
			saveDefaultInput();
			return FALSE;
		}
		// <== default input mapping

		if (Id == IDC_INPD_GI && Notify == CBN_SELCHANGE) {
			int nGi = SendMessage(hInpdGi, CB_GETCURSEL, 0, 0);
			if (nGi == CB_ERR) {
				SendMessage(hInpdPci, CB_SETCURSEL, (WPARAM)-1, 0);
				SendMessage(hInpdAnalog, CB_SETCURSEL, (WPARAM)-1, 0);
				disablePresets();
				return FALSE;
			}

			int nPci = playerDefaultControls[nGi] & 0x0F;
			SendMessage(hInpdPci, CB_SETCURSEL, nPci, 0);
			EnableWindow(hInpdPci, TRUE);

			if (nPci > 5) {
				SendMessage(hInpdAnalog, CB_SETCURSEL, (WPARAM)-1, 0);
				EnableWindow(hInpdAnalog, FALSE);
			} else {
				initAnalogOptions(nGi, nPci);
				EnableWindow(hInpdAnalog, TRUE);
			}

			EnableWindow(GetDlgItem(hInpdDlg, IDC_INPD_DEFAULT), TRUE);
			EnableWindow(GetDlgItem(hInpdDlg, IDC_INPD_USE), TRUE);
			return FALSE;
		}

		if (Id == IDC_INPD_PCI && Notify == CBN_SELCHANGE) {
			int nGi = SendMessage(hInpdGi, CB_GETCURSEL, 0, 0);
			if (nGi == CB_ERR) {
				return FALSE;
			}
			int nPci = SendMessage(hInpdPci, CB_GETCURSEL, 0, 0);
			if (nPci == CB_ERR) {
				return FALSE;
			}

			if (nPci > 7) {
				EnableWindow(GetDlgItem(hInpdDlg, IDC_INPD_DEFAULT), TRUE);
				EnableWindow(GetDlgItem(hInpdDlg, IDC_INPD_USE), TRUE);

				SendMessage(hInpdAnalog, CB_SETCURSEL, (WPARAM)-1, 0);
				EnableWindow(hInpdAnalog, FALSE);
			} else {
				EnableWindow(hInpdAnalog, TRUE);
				initAnalogOptions(nGi, nPci);

				if (SendMessage(hInpdAnalog, CB_GETCURSEL, 0, 0) != CB_ERR) {
					EnableWindow(GetDlgItem(hInpdDlg, IDC_INPD_DEFAULT), TRUE);
					EnableWindow(GetDlgItem(hInpdDlg, IDC_INPD_USE), TRUE);
				} else {
					EnableWindow(GetDlgItem(hInpdDlg, IDC_INPD_DEFAULT), FALSE);
					EnableWindow(GetDlgItem(hInpdDlg, IDC_INPD_USE), FALSE);
				}
			}

			return FALSE;
		}

		if (Id == IDC_INPD_ANALOG && Notify == CBN_SELCHANGE) {
			if (SendMessage(hInpdAnalog, CB_GETCURSEL, 0, 0) != CB_ERR) {
				EnableWindow(GetDlgItem(hInpdDlg, IDC_INPD_DEFAULT), TRUE);
				EnableWindow(GetDlgItem(hInpdDlg, IDC_INPD_USE), TRUE);
			}
			return FALSE;
		}
	}

	// ==> save preset if rbutton clicked, added by regret
	if (msg == WM_RBUTTONUP) {
		return onRButtonUp(hDlg, LOWORD(lParam), HIWORD(lParam));
	}
	// <== save preset

	if (msg == WM_NOTIFY && lParam != 0) {
		int Id = LOWORD(wParam);
		NMHDR* pnm = (NMHDR*)lParam;

		if (Id == IDC_INPD_LIST && pnm->code == LVN_ITEMACTIVATE) {
			listItemActivate();
		}

		if (Id == IDC_INPD_LIST && pnm->code == LVN_KEYDOWN) {
			NMLVKEYDOWN *pnmkd = (NMLVKEYDOWN*)lParam;
			if (pnmkd->wVKey == VK_DELETE) {
				listItemDelete();
			}
		}

		if (Id == IDC_INPD_LIST && pnm->code == NM_CUSTOMDRAW) {
			NMLVCUSTOMDRAW* plvcd = (NMLVCUSTOMDRAW*)lParam;

			switch (plvcd->nmcd.dwDrawStage) {
				case CDDS_PREPAINT:
                    SetWindowLongPtr(hInpdDlg, DWLP_MSGRESULT, CDRF_NOTIFYITEMDRAW);
					return 1;

				case CDDS_ITEMPREPAINT:
					if (plvcd->nmcd.dwItemSpec < gameInpCount) {
						if (gameInput[plvcd->nmcd.dwItemSpec].nType & BIT_GROUP_CONSTANT) {

							if (gameInput[plvcd->nmcd.dwItemSpec].nInput == 0) {
								plvcd->clrTextBk = RGB(0xDF, 0xDF, 0xDF);
								SetWindowLongPtr(hInpdDlg, DWLP_MSGRESULT, CDRF_NEWFONT);
								return 1;
							}

							if (gameInput[plvcd->nmcd.dwItemSpec].nType == BIT_DIPSWITCH) {
								plvcd->clrTextBk = RGB(0xFF, 0xEF, 0xD7);
								SetWindowLongPtr(hInpdDlg, DWLP_MSGRESULT, CDRF_NEWFONT);
								return 1;
							}
						}
					} else {
						if (gameInput[plvcd->nmcd.dwItemSpec].Macro.nMode) {
							plvcd->clrTextBk = RGB(0xFF, 0xCF, 0xCF);
						} else {
							plvcd->clrTextBk = RGB(0xFF, 0xEF, 0xEF);
						}
						SetWindowLongPtr(hInpdDlg, DWLP_MSGRESULT, CDRF_NEWFONT);
						return 1;
					}
					return 1;
			}
		}

		return FALSE;
	}

	return FALSE;
}

int inpdCreate()
{
	if (driverOkay == 0) {
		return 1;
	}

	if (hInpdDlg) {
		// already open so just reactivate the window
		SetActiveWindow(hInpdDlg);
		return 0;
	}

	hInpdDlg = localiseCreateDialog(IDD_INPD, hScrnWnd, (DLGPROC)inpDialogProc);
	if (hInpdDlg == NULL) {
		return 1;
	}
	dialogManager.add(IDD_INPD, hInpdDlg);

	wndInMid(hInpdDlg, hScrnWnd);
	ShowWindow(hInpdDlg, SW_NORMAL);
	return 0;
}
